package itdep.common.services.impl;

import itdep.common.domain.Entity;
import itdep.common.domain.EntityDao;
import itdep.common.domain.EntityFilter;
import itdep.common.services.MutableEntityService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Ivan Khalopik
 * @version $Revision: 4 $ $Date: 2010-06-10 13:13:16 +0000 (Thu, 10 Jun 2010) $
 */
public class MutableEntityServiceImpl<PK extends Serializable, E extends Entity<PK>, F extends EntityFilter<PK>, D extends EntityDao<PK, E>>
		extends EntityServiceImpl<PK, E, F, D>
		implements MutableEntityService<PK, E, F> {

	public enum DeleteMode {
		HIDE, PURGE, DENY
	}

	private DeleteMode defaultDeleteMode = DeleteMode.PURGE;

	public MutableEntityServiceImpl(final D dao) {
		super(dao);
	}

	public E create() {
		return dao().create();
	}

	@Transactional
	@Secured(value = "ACL_CREATE")
	public void save(final E entity) {
		dao().saveOrUpdate(entity);
	}

	@Transactional
	@Secured(value = "ACL_WRITE")
	public void update(final E entity) {
		dao().update(entity);
	}

	@Transactional
	@Secured(value = "ACL_WRITE")
	public void merge(final E entity) {
		dao().merge(entity);
	}

	@Transactional
	@Secured(value = "ACL_DELETE")
	public void delete(final E entity) {
		delete(entity, new Date(), defaultDeleteMode);
	}

	protected void delete(final E entity, final Date date, final DeleteMode mode) {
		switch (mode) {
		case PURGE:
			dao().delete(entity);
			break;
		case HIDE:
			hideWithCildren(entity);
			dao().update(entity);
			break;
		case DENY:
			throw new IllegalStateException("Entity " + entity + " could not be deleted");
		}
	}

	private Set<Entity> hideWithCildren(Entity<?> e) {
		final Set<Entity> result = new HashSet<Entity>();
		if (!e.isDeleted()) {
			e.setDeletedAt(new Date());
			result.add(e);
			for (Entity<?> entity : e.getChildren()) {
				result.addAll(hideWithCildren(entity));
			}
		}
		return result;
	}
}
